/*@parser.c::*/
/** 
 * Simplified Pascal in the EBNF:
 * 
 * program -> PROGRAM ID ';' block '.'
 * 
 * block -> declarations modules stmtblock
 * 
 * declarations -> 
 *    [CONST ID = constant';' {ID = constant';'}]
 *    [TYPE ID = type';' {ID = type';'}]
 *    [VAR idlist ':' type';' {idlist ':' type';'}]
 * 
 * modules -> {procedure | function}
 * 
 * procedure -> PROCEDURE ID formalparam';' block ';'
 * 
 * function -> FUNCTION ID formalparam ':' typeid ';'block';'
 * 
 * stmtblock -> BEGIN stmtlist END
 * 
 * stmtlist -> stmt {';' stmt}
 * 
 * stmt -> stmtblock | ifstmt | whilestmt | repstmt | forstmt
 *      | gotostmt | casestmt | idstmt
 */
#include <parser.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <tokens.h>
#include <keywords.h>
#include <lexer.h>

#define MAXIDLIST 128

/* program -> PROGRAM ID ';' block '.'*/
void program(void)
{
  match(PROGRAM); 
  match(ID);
  match(';');
  block();
  match('.');
}

/* block -> declarations stmtblock*/
void block(void)
{
  declarations();
  modules();
  stmtblock();
}

/* declarations -> [VAR idlist ':' type';' {idlist ':' type';'}]
 *                 [CONST ID = constant';' {ID = constant';'}]
 */
void declarations(void)
{
  if( lookahead == CONST ){
    match(CONST);
rep_const:
    match(ID);
    match('=');
    //constant();
    if( lookahead == ID ) goto rep_const;
  }
/* Variable declaration is here */
  if( lookahead == VAR ){
    match(VAR); 
rep_idlist:
    idlist();
    match(':');
    type();
    match(';');
    if(lookahead==ID)goto rep_idlist;
  }
}
/* modules -> {procedure | function} */
void modules(void)
{
  while( lookahead == PROCEDURE || lookahead == FUNCTION ){
    if( lookahead == PROCEDURE ) procedure();
    else function();
  }
}
/* procedure ->  PROCEDURE ID formalparam';' block ';' */
void procedure(void)
{
  match(PROCEDURE);
  match(ID);
  formalparam();
  match(';');
  declarations();
  stmtblock();
  match(';');
}
/* function -> FUNCTION ID formalparam ':' type ';'block';' */
void function(void)
{
  match(FUNCTION);
  match(ID);
  formalparam();
  match(':');
  type();
  match(';');
  declarations();
  stmtblock();  
  match(';');
}

/* idlist -> ID {','ID}*/
void idlist(void)
{
  match(ID);
  while(lookahead==','){
    match(',');
    match(ID);
  }
}
/* type -> INTEGER | REAL */
void type(void)
{
  switch(lookahead){
    case INTEGER:
      match(INTEGER);
      break;
    default:
      match(REAL);
  }
}
/* stmt -> stmtblock | ifstmt | whilestmt | repstmt | idstmt | "" */
void stmt(void)
{
  switch(lookahead){
    case BEGIN:
      stmtblock();
      break;
    case IF:
      ifstmt();
      break;
    case WHILE:
      whilestmt();
      break;
    case REPEAT:
      repstmt();
      break;
    case ID:
      idstmt();
      break;
  }
}
/* stmtblock -> BEGIN stmtlist END */
void stmtblock(void)
{
  match(BEGIN);stmtlist();match(END);
}

/* stmtblock -> stmt { ';' stmt }*/
void stmtlist(void)
{
  stmt();
  while(lookahead==';'){
    match(';');
    stmt();
  }
}
/* ifstmt -> IF expression THEN stmt [ELSE stmt] */
void ifstmt(void)
{
  match(IF);
  expression();
  match(THEN);
  stmt();
  if(lookahead==ELSE) {
    match(ELSE);	
    stmt();
  }	
}
/* whilestmt -> WHILE expression DO stmt */
void whilestmt(void)
{
  match(WHILE);
  expression();
  match(DO);
  stmt();
}
/* repstmt -> REPEAT stmtlist UNTIL expression */
void repstmt(void)
{
  match(REPEAT);
  stmtlist();
  match(UNTIL);
  expression();
}
/* idstmt -> assgnmt | proccall */
void idstmt(void)
{
  match(ID);
  if( lookahead == '(' || lookahead != ASSGNMT ) 
    proccall();
  else{
    assgnmt();
  }  
}
/* assgnmt -> (variable | ID ) ':=' expression */
void assgnmt(void)
{
    variable();
    match(ASSGNMT);
    expression();
}
/* proccall -> ID ['(' exprlist ')']  */
void proccall(void){
    if ( lookahead == '(' ){
      match('(');
      exprlist();
      match(')');
    }
}
/* variable -> ID { '[' exprlist ']' | '^'  } */
void variable(void){
   while ( lookahead == '[' || lookahead == '^' ){
     if ( lookahead == '[' ){
       match('[');
       exprlist();
       match(']');
     }
     else
       match('^');
   }
}
int isrelationalop(token_t token)
{
  switch(token){
    case '=':
    case '>':
    case '<':
    case GEQ:
    case LEQ:
    case NEQ:
      return token;
  }
  return 0;
}
int isaddop(token_t token)
{
  switch(token){
    case '+':
    case '-':
    case OR:
      return token;
  }
  return 0;
}
int ismulop(token_t token)
{
  switch(token){
    case '*':
    case '/':
    case AND:
    case DIV:
    case MOD:
      return token;
  }
  return 0;
}
/* expression -> expr [RELOP expr]*/
void expression(void)
{
  expr();
  if(isrelationalop(lookahead)){
    match(lookahead); expr();
  }
}
/* expr -> ['-'] term {ADD term} */
void expr(void)
{
  if(lookahead=='-'){
    match('-');
  }
  term();
  while( lookahead == ADD || lookahead == OR ){
    match(lookahead); 
    term();
  }
}
/* term -> factor {MUL factor} */
void term(void)
{
  factor();
  while( lookahead == MUL || lookahead == AND ){
    match(lookahead); 
    factor();
  }
}
/*factor -> UINT | FLOAT | ID ['(' exprlist ')'] | '(' expression ')' | NOT factor */
void factor(void){
    switch(lookahead){
      case UINT:
	match(UINT);
	break;
      case FLOAT:
	match(FLOAT);
	break;
      case BOOL:
	match(BOOL);
	break;
      case ID:
        match(ID);
        if(lookahead=='('){
          match('(');
	  exprlist();
	  match(')');
        }
        break;
      case '(':
        match('(');expression();match(')');
	break;
      default:
        match(NOT);
	factor();
    }
}
/*exprlist -> expression {',' expression}*/
void exprlist(void)
{
  expression();
  while(lookahead==','){
    match(',');
    expression();
  }
}
/*formalparam -> '(' varlist ':' typespec ';' { varlist ':' typespec ';' } ')' */
void formalparam(){
  if ( lookahead == '(' ){
    match('(');
    if ( lookahead == VAR )
      match(VAR);
    
    idlist();
    match(':');
    type();
    while ( lookahead == ';' ){
      match(';');
      if ( lookahead == VAR )
        match(VAR);
      
      idlist();
      match(':');
      type();
    }
   match(')');
  }
}